home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
JFC.bin
/
BasicListUI.java
< prev
next >
Wrap
Text File
|
1998-06-30
|
37KB
|
1,108 lines
/*
* @(#)BasicListUI.java 1.28 98/04/14
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.plaf.basic;
import com.sun.java.swing.*;
import com.sun.java.swing.event.*;
import com.sun.java.swing.plaf.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.io.Serializable;
/**
* A Windows L&F implementation of ListUI.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version 1.28 04/14/98
* @author Hans Muller
*/
public class BasicListUI extends ListUI implements Serializable
{
/**
* True if the JList has the focus. We cache this value rather than
* relying on JComponent.hasFocus() as it's consulted per cell at
* paint time.
*
* #see InputListener
*/
protected transient boolean hasFocus = false;
protected ListSelectionListener selectionL;
protected ListDataListener dataL;
protected PropertyChangeListener propertyL;
protected InputListener inputL;
protected JList list = null;
protected CellRendererPane rendererPane;
// PENDING(hmuller) need a doc pointer to #getRowHeight, #maybeUpdateLayout
protected int[] cellHeights = null;
protected int cellHeight = -1;
protected int cellWidth = -1;
protected int updateLayoutStateNeeded = modelChanged;
/* The bits below define JList property changes that affect layout.
* When one of these properties changes we set a bit in
* updateLayoutStateNeeded. The change is dealt with lazily, see
* maybeUpdateLayout. Changes to the JLists model, e.g. the
* models length changed, are handled similarly, see DataListener.
*/
protected final static int modelChanged = 1 << 0;
protected final static int selectionModelChanged = 1 << 1;
protected final static int fontChanged = 1 << 2;
protected final static int fixedCellWidthChanged = 1 << 3;
protected final static int fixedCellHeightChanged = 1 << 4;
protected final static int prototypeCellValueChanged = 1 << 5;
protected final static int cellRendererChanged = 1 << 6;
/**
* Paint one List cell: compute the relevant state, get the "rubber stamp"
* cell renderer component, and then use the CellRendererPane to paint it.
* Subclasses may want to override this method rather than paint().
*
* @see #paint
*/
protected void paintCell(
Graphics g,
int row,
Rectangle rowBounds,
ListCellRenderer cellRenderer,
ListModel dataModel,
ListSelectionModel selModel,
int leadIndex)
{
Object value = dataModel.getElementAt(row);
boolean cellHasFocus = hasFocus && (row == leadIndex);
boolean isSelected = selModel.isSelectedIndex(row);
Component rendererComponent =
cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
int cx = rowBounds.x;
int cy = rowBounds.y;
int cw = rowBounds.width;
int ch = rowBounds.height;
rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
}
/**
* Paint the rows that intersect the Graphics objects clipRect. This
* method calls paintCell as necessary. Subclasses
* may want to override these methods.
*
* @see #paintCell
*/
public void paint(Graphics g, JComponent c)
{
maybeUpdateLayoutState();
ListCellRenderer renderer = list.getCellRenderer();
ListModel dataModel = list.getModel();
ListSelectionModel selModel = list.getSelectionModel();
if ((renderer == null) || (dataModel.getSize() == 0)) {
return;
}
/* Compute the area we're going to paint in terms of the affected
* rows (firstPaintRow, lastPaintRow), and the clip bounds.
*/
Rectangle paintBounds = g.getClipBounds();
int firstPaintRow = convertYToRow(paintBounds.y);
int lastPaintRow = convertYToRow((paintBounds.y + paintBounds.height) - 1);
if (firstPaintRow == -1) {
firstPaintRow = 0;
}
if (lastPaintRow == -1) {
lastPaintRow = dataModel.getSize() - 1;
}
Rectangle rowBounds = getCellBounds(list, firstPaintRow, firstPaintRow);
if (rowBounds == null) {
return;
}
int leadIndex = list.getLeadSelectionIndex();
for(int row = firstPaintRow; row <= lastPaintRow; row++) {
rowBounds.height = getRowHeight(row);
/* Set the clip rect to be the intersection of rowBounds
* and paintBounds and then paint the cell.
*/
g.setClip(rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height);
g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width, paintBounds.height);
paintCell(g, row, rowBounds, renderer, dataModel, selModel, leadIndex);
rowBounds.y += rowBounds.height;
}
}
/**
* The preferredSize of a list is total height of the rows
* and the maximum width of the cells. If JList.fixedCellHeight
* is specified then the total height of the rows is just
* (cellVerticalMargins + fixedCellHeight) * model.getSize() where
* rowVerticalMargins is the space we allocate for drawing
* the yellow focus outline. Similarly if JListfixedCellWidth is
* specified then we just use that plus the horizontal margins.
*
* @param c The JList component.
* @return The total size of the list.
*/
public Dimension getPreferredSize(JComponent c) {
maybeUpdateLayoutState();
int lastRow = list.getModel().getSize() - 1;
if (lastRow < 0) {
return new Dimension(0, 0);
}
Insets insets = list.getInsets();
int width = cellWidth + insets.left + insets.right;
int height = convertRowToY(lastRow) + getRowHeight(lastRow) + insets.bottom;
return new Dimension(width, height);
}
/**
* @returns The preferred size.
* @see #getPreferredSize
*/
public Dimension getMinimumSize(JComponent c) {
return getPreferredSize(c);
}
/**
* @returns The preferred size.
* @see #getPreferredSize
*/
public Dimension getMaximumSize(JComponent c) {
return getPreferredSize(c);
}
/**
* Selected the previous row and force it to be visible.
* Called by the KeyEvent.VK_UP keyboard action.
*
* @see #registerKeyboardActions
* @see JList#ensureIndexIsVisible
*/
protected void selectPreviousIndex() {
int s = list.getSelectedIndex();
if(s > 0) {
s -= 1;
list.setSelectedIndex(s);
list.ensureIndexIsVisible(s);
}
}
/**
* Selected the previous row and force it to be visible.
* Called by the KeyEvent.VK_DOWN keyboard action.
*
* @see #registerKeyboardActions
* @see JList#ensureIndexIsVisible
*/
protected void selectNextIndex()
{
int s = list.getSelectedIndex();
if((s + 1) < list.getModel().getSize()) {
s += 1;
list.setSelectedIndex(s);
list.ensureIndexIsVisible(s);
}
}
/**
* Register keyboard actions for the up and down arrow keys. The
* actions just call out to protected methods, subclasses that
* want to override or extend keyboard behavior should consider
* just overriding those methods. This method is called at
* installUI() time.
*
* @see #selectPreviousIndex
* @see #selectNextIndex
* @see #installUI
*/
protected void registerKeyboardActions()
{
/* SelectPreviousRow - UpArrow Key
*/
{
AbstractAction action = new AbstractAction("SelectPreviousRow") {
public void actionPerformed(ActionEvent e) { selectPreviousIndex(); }
public boolean isEnabled() { return true; }
};
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
list.registerKeyboardAction(action, key, JComponent.WHEN_FOCUSED);
}
/* SelectNextRow - DownArrow Key
*/
{
AbstractAction action = new AbstractAction("SelectNextRow") {
public void actionPerformed(ActionEvent e) { selectNextIndex(); }
public boolean isEnabled() { return true; }
};
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
list.registerKeyboardAction(action, key, JComponent.WHEN_FOCUSED);
}
}
/**
* Unregister keyboard actions for the up and down arrow keys.
* This method is called at uninstallUI() time - subclassess should
* ensure that all of the keyboard actions registered at installUI
* time are removed here.
*
* @see #selectPreviousIndex
* @see #selectNextIndex
* @see #installUI
*/
protected void unregisterKeyboardActions() {
list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
}
/**
* Create and install the listeners for the JList, its model, and its
* selectionModel. This method is called at installUI() time.
*
* @see #installUI
* @see #removeListListeners
*/
protected void addListListeners()
{
inputL = createInputListener();
list.addMouseListener(inputL);
list.addMouseMotionListener(inputL);
/* When a property changes that effects layout we set
* updateLayoutStateNeeded to the appropriate code. We also
* add/remove List data model listeners when the "model"
* property changes.
*/
propertyL = createPropertyListener();
list.addPropertyChangeListener(propertyL);
/* When a mouseDown event occurs have the list requestFocus().
* We watch the focusIn/Out events so that the yellow leadIndex
* hightlight can be painted or not as neccessary.
*/
list.addFocusListener(inputL);
dataL = createDataListener();
ListModel model = list.getModel();
if (model != null) {
model.addListDataListener(dataL);
}
selectionL = createSelectionListener();
ListSelectionModel selectionModel = list.getSelectionModel();
if (selectionModel != null) {
selectionModel.addListSelectionListener(selectionL);
}
}
/**
* Remove the listeners for the JList, its model, and its
* selectionModel. All of the listener fields, are reset to
* null here. This method is called at uninstallUI() time,
* it should be kept in sync with addListListeners.
*
* @see #uninstallUI
* @see #addListListeners
*/
protected void removeListListeners()
{
list.removeMouseListener(inputL);
list.removeMouseMotionListener(inputL);
list.removePropertyChangeListener(propertyL);
list.removeFocusListener(inputL);
ListModel model = list.getModel();
if (model != null) {
model.removeListDataListener(dataL);
}
ListSelectionModel selectionModel = list.getSelectionModel();
if (selectionModel != null) {
selectionModel.removeListSelectionListener(selectionL);
}
SelectionListener selectionL = null;
DataListener dataL = null;
InputListener inputL = null;
PropertyListener propertyL = null;
}
/**
* Initialize JList properties, e.g. font, foreground, and background,
* and add the CellRendererPane. The font, foreground, and background
* properties are only set if their current value is either null
* or a UIResource, other properties are set if the current
* value is null.
*
* @see #unconfigureList
* @see #installUI
* @see CellRendererPane
*/
protected void configureList()
{
list.setLayout(null);
LookAndFeel.installBorder(list, "List.border");
LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");
if (list.getCellRenderer() == null) {
list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer")));
}
Color sbg = list.getSelectionBackground();
if (sbg == null || sbg instanceof UIResource) {
list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
}
Color sfg = list.getSelectionForeground();
if (sfg == null || sfg instanceof UIResource) {
list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
}
rendererPane = new CellRendererPane();
list.add(rendererPane);
}
/**
* Set the JList properties that haven't been explicitly overriden to
* null. A property is considered overridden if its current value
* is not a UIResource.
*
* @see #configureList
* @see #uninstallUI
* @see CellRendererPane
*/
protected void unconfigureList()
{
list.remove(rendererPane);
rendererPane = null;
if (list.getCellRenderer() instanceof UIResource) {
list.setCellRenderer(null);
}
}
/**
* Initializes <code>this.list</code> by calling <code>configureList()</code>,
* <code>addListListeners()</code>, and <code>registerKeyboardActions()</code>
* in order.
*
* @see #configureList
* @see #addListListeners
* @see #registerKeyboardActions
*/
public void installUI(JComponent list)
{
this.list = (JList)list;
configureList();
addListListeners();
registerKeyboardActions();
}
/**
* Uninitializes <code>this.list</code> by calling <code>removeListListeners()</code>,
* <code>unregisterKeyboardActions()</code>, and <code>unconfigureList()</code>
* in order. Sets this.list to null.
*
* @see #removeListListeners
* @see #unregisterKeyboardActions
* @see #unconfigureList
*/
public void uninstallUI(JComponent c)
{
removeListListeners();
unregisterKeyboardActions();
unconfigureList();
cellWidth = cellHeight = -1;
cellHeights = null;
this.list = null;
}
/**
* Returns a new instance of BasicListUI. BasicListUI delegates are
* allocated one per JList.
*
* @return A new ListUI implementation for the Windows look and feel.
*/
public static ComponentUI createUI(JComponent list) {
return new BasicListUI();
}
/**
* @return The index of the cell at location, or -1.
* @see ListUI#locationToIndex
*/
public int locationToIndex(JList list, Point location) {
maybeUpdateLayoutState();
return convertYToRow(location.y);
}
/**
* @return The origin of the index'th cell.
* @see ListUI#indexToLocation
*/
public Point indexToLocation(JList list, int index) {
maybeUpdateLayoutState();
return new Point(0, convertRowToY(index));
}
/**
* @return The bounds of the index'th cell.
* @see ListUI#getCellBounds
*/
public Rectangle getCellBounds(JList list, int index1, int index2) {
maybeUpdateLayoutState();
int minIndex = Math.min(index1, index2);
int maxIndex = Math.max(index1, index2);
int minY = convertRowToY(minIndex);
int maxY = convertRowToY(maxIndex);
if ((minY == -1) || (maxY == -1)) {
return null;
}
Insets insets = list.getInsets();
int x = insets.left;
int y = minY;
int w = list.getWidth() - (insets.left + insets.right);
int h = (maxY + getRowHeight(maxIndex)) - minY;
return new Rectangle(x, y, w, h);
}
// PENDING(hmuller) explain the cell geometry abstraction in
// the getRowHeight javadoc
/**
* Returns the height of the specified row based on the current layout.
*
* @return The specified row height or -1 if row isn't valid.
* @see #convertYToRow
* @see #convertRowToY
* @see #updateLayoutState
*/
protected int getRowHeight(int row)
{
if ((row < 0) || (row >= list.getModel().getSize())) {
return -1;
}
// PENDING(hmuller) hacked around off by one. Maybe the >= above should be >
return (cellHeights == null) ? cellHeight : ((row < cellHeights.length) ? cellHeights[row] : -1);
}
/**
* Convert the JList relative coordinate to the row that contains it,
* based on the current layout. If y0 doesn't fall within any row,
* return -1.
*
* @return The row that contains y0, or -1.
* @see #getRowHeight
* @see #updateLayoutState
*/
protected int convertYToRow(int y0)
{
int nrows = list.getModel().getSize();
Insets insets = list.getInsets();
if (cellHeights == null) {
int row = (cellHeight == 0) ? 0 : ((y0 - insets.top) / cellHeight);
return ((row < 0) || (row >= nrows)) ? -1 : row;
}
else {
int y = insets.top;
int row = 0;
for(int i = 0; i < nrows; i++) {
if ((y0 >= y) && (y0 < y + cellHeights[i])) {
return row;
}
y += cellHeights[i];
row += 1;
}
return -1;
}
}
/**
* Return the JList relative Y coordinate of the origin of the specified
* row or -1 if row isn't valide.
*
* @return The Y coordinate of the origin of row, or -1.
* @see #getRowHeight
* @see #updateLayoutState
*/
protected int convertRowToY(int row)
{
int nrows = list.getModel().getSize();
Insets insets = list.getInsets();
if ((row < 0) || (row > nrows)) {
return -1;
}
if (cellHeights == null) {
return insets.top + (cellHeight * row);
}
else {
int y = insets.top;
for(int i = 0; i < row; i++) {
y += cellHeights[i];
}
return y;
}
}
/**
* If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
* updateLayoutStateNeeded. This method should be called by methods
* before doing any computation based on the geometry of the list.
* For example it's the first call in paint() and getPreferredSize().
*
* @see #updateLayoutState
*/
protected void maybeUpdateLayoutState()
{
if (updateLayoutStateNeeded != 0) {
updateLayoutState();
updateLayoutStateNeeded = 0;
}
}
/**
* Recompute the value of cellHeight or cellHeights based
* and cellWidth, based on the current font and the current
* values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
*
* @see #maybeUpdateLayoutState
*/
protected void updateLayoutState()
{
/* If both JList fixedCellWidth and fixedCellHeight have been
* set, then initialize cellWidth and cellHeight, and set
* cellHeights to null.
*/
int fixedCellHeight = list.getFixedCellHeight();
int fixedCellWidth = list.getFixedCellWidth();
cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
if (fixedCellHeight != -1) {
cellHeight = fixedCellHeight;
cellHeights = null;
}
else {
cellHeight = -1;
cellHeights = new int[list.getModel().getSize()];
}
/* If either of JList fixedCellWidth and fixedCellHeight haven't
* been set, then initialize cellWidth and cellHeights by
* scanning through the entire model. Note: if the renderer is
* null, we just set cellWidth and cellHeights[*] to zero,
* if they're not set already.
*/
if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
ListModel dataModel = list.getModel();
int dataModelSize = dataModel.getSize();
ListCellRenderer renderer = list.getCellRenderer();
if (renderer != null) {
for(int index = 0; index < dataModelSize; index++) {
Object value = dataModel.getElementAt(index);
Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
rendererPane.add(c);
Dimension cellSize = c.getPreferredSize();
if (fixedCellWidth == -1) {
cellWidth = Math.max(cellSize.width, cellWidth);
}
if (fixedCellHeight == -1) {
cellHeights[index] = cellSize.height;
}
}
}
else {
if (cellWidth == -1) {
cellWidth = 0;
}
for(int index = 0; index < dataModelSize; index++) {
cellHeights[index] = 0;
}
}
}
list.invalidate();
}
/**
* Mouse input, and focus handling for JList. An instance of this
* class is added to the appropriate java.awt.Component lists
* at installUI() time. Note keyboard input is handled with JComponent
* KeyboardActions, see registerKeyboardActions().
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @see #createInputListener
* @see #registerKeyboardActions
* @see #installUI
*/
protected class InputListener extends MouseAdapter
implements MouseMotionListener, FocusListener, Serializable
{
public void mousePressed(MouseEvent e)
{
int row = convertYToRow(e.getY());
if (row != -1) {
list.setValueIsAdjusting(true);
int anchorIndex = list.getAnchorSelectionIndex();
if (e.isControlDown()) {
if (list.isSelectedIndex(row)) {
list.removeSelectionInterval(row, row);
}
else {
list.addSelectionInterval(row, row);
}
}
else if (e.isShiftDown() && (anchorIndex != -1)) {
list.setSelectionInterval(anchorIndex, row);
}
else {
list.setSelectionInterval(row, row);
}
}
if (!hasFocus) {
list.requestFocus();
}
}
public void mouseDragged(MouseEvent e) {
if (e.isShiftDown() || e.isControlDown()) {
return;
}
int row = convertYToRow(e.getY());
if (row != -1) {
Rectangle cellBounds = getCellBounds(list, row, row);
if (cellBounds != null) {
list.scrollRectToVisible(cellBounds);
list.setSelectionInterval(row, row);
}
}
}
public void mouseMoved(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
if (e.isShiftDown() || e.isControlDown()) {
return;
}
int row = convertYToRow(e.getY());
if (row != -1) {
list.setSelectionInterval(row, row);
}
list.setValueIsAdjusting(false);
}
protected void repaintCellFocus()
{
int leadIndex = list.getLeadSelectionIndex();
if (leadIndex != -1) {
Rectangle r = getCellBounds(list, leadIndex, leadIndex);
if (r != null) {
list.repaint(r.x, r.y, r.width, r.height);
}
}
}
/* The focusGained() focusLost() methods run when the JList
* focus changes.
*/
public void focusGained(FocusEvent e) {
hasFocus = true;
repaintCellFocus();
}
public void focusLost(FocusEvent e) {
hasFocus = false;
repaintCellFocus();
}
}
/**
* Creates a delegate that implements MouseListener, MouseMotionListener,
* and FocusListener. The delegate is added to the corresponding
* java.awt.Component listener lists at installUI() time. Subclasses can
* override this method to return a custom InputListener, e.g.
* <pre>
* class MyListUI extends BasicListUI {
* protected InputListener <b>createInputListener</b>() {
* return new MyInputListener();
* }
* public class MyInputListener extends InputListener {
* public void mouseMoved(MouseEvent e) {
* // do some extra work when the mouse moves
* super.mouseMoved(e);
* }
* }
* }
* </pre>
*
* @see InputListener
* @see #installUI
*/
protected InputListener createInputListener() {
return new InputListener();
}
/**
* The ListSelectionListener that's added to the JLists selection
* model at installUI time, and whenever the JList.selectionModel property
* changes. When the selection changes we repaint the affected rows.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @see #createSelectionListener
* @see #getCellBounds
* @see #installUI
*/
public class SelectionListener implements ListSelectionListener, Serializable
{
public void valueChanged(ListSelectionEvent e)
{
maybeUpdateLayoutState();
int minY = convertRowToY(e.getFirstIndex());
int maxY = convertRowToY(e.getLastIndex());
if ((minY == -1) || (maxY == -1)) {
list.repaint(0, 0, list.getWidth(), list.getHeight());
}
else {
maxY += getRowHeight(e.getLastIndex());
list.repaint(0, minY, list.getWidth(), maxY - minY);
}
}
}
/**
* Creates an instance of ListSelectionListener that's added to
* the JLists by selectionModel as needed. Subclasses can override
* this method to return a custom ListSelectionListener, e.g.
* <pre>
* class MyListUI extends BasicListUI {
* protected ListSelectionListener <b>createSelectionListener</b>() {
* return new MySelectionListener();
* }
* public class MySelectionListener extends SelectionListener {
* public void valueChanged(ListSelectionEvent e) {
* // do some extra work when the selection changes
* super.valueChange(e);
* }
* }
* }
* </pre>
*
* @see SelectionListener
* @see #installUI
*/
protected ListSelectionListener createSelectionListener() {
return new SelectionListener();
}
/**
* The ListDataListener that's added to the JLists model at
* installUI time, and whenever the JList.model property changes.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @see JList#getModel
* @see #maybeUpdateLayout
* @see #createDataListener
* @see #installUI
*/
public class DataListener implements ListDataListener, Serializable
{
public void intervalAdded(ListDataEvent e) {
updateLayoutStateNeeded = modelChanged;
int minIndex = Math.min(e.getIndex0(), e.getIndex1());
int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
/* Sync the SelectionModel with the DataModel.
*/
ListSelectionModel sm = list.getSelectionModel();
if (sm != null) {
sm.insertIndexInterval(minIndex, maxIndex - minIndex, true);
}
/* Repaint the entire list, from the origin of
* the first added cell, to the bottom of the
* component.
*/
int y = Math.max(0, convertRowToY(minIndex));
int h = list.getHeight() - y;
list.revalidate();
list.repaint(0, y, list.getWidth(), h);
}
public void intervalRemoved(ListDataEvent e)
{
updateLayoutStateNeeded = modelChanged;
/* Sync the SelectionModel with the DataModel.
*/
ListSelectionModel sm = list.getSelectionModel();
if (sm != null) {
sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
}
/* Repaint the entire list, from the origin of
* the first removed cell, to the bottom of the
* component.
*/
int minIndex = Math.min(e.getIndex0(), e.getIndex1());
int y = Math.max(0, convertRowToY(minIndex));
int h = list.getHeight() - y;
list.revalidate();
list.repaint(0, y, list.getWidth(), h);
}
public void contentsChanged(ListDataEvent e) {
updateLayoutStateNeeded = modelChanged;
list.revalidate();
list.repaint();
}
}
/**
* Creates an instance of ListDataListener that's added to
* the JLists by model as needed. Subclasses can override
* this method to return a custom ListDataListener, e.g.
* <pre>
* class MyListUI extends BasicListUI {
* protected ListDataListener <b>createDataListener</b>() {
* return new MyDataListener();
* }
* public class MyDataListener extends DataListener {
* public void contentsChanged(ListDataEvent e) {
* // do some extra work when the models contents change
* super.contentsChange(e);
* }
* }
* }
* </pre>
*
* @see DataListener
* @see JList#getModel
* @see #installUI
*/
protected ListDataListener createDataListener() {
return new DataListener();
}
/**
* The PropertyChangeListener that's added to the JList at
* installUI time. When the value of a JList property that
* affects layout changes, we set a bit in updateLayoutStateNeeded.
* If the JLists model changes we additionally remove our listeners
* from the old model. Likewise for the JList selectionModel.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @see #maybeUpdateLayout
* @see #createPropertyListener
* @see #installUI
*/
public class PropertyListener implements PropertyChangeListener, Serializable
{
public void propertyChange(PropertyChangeEvent e)
{
String propertyName = e.getPropertyName();
/* If the JList.model property changes, remove our listener,
* dataL from the old model and add it to the new one.
*/
if (propertyName.equals("model")) {
ListModel oldModel = (ListModel)e.getOldValue();
ListModel newModel = (ListModel)e.getNewValue();
if (oldModel != null) {
oldModel.removeListDataListener(dataL);
}
if (newModel != null) {
newModel.addListDataListener(dataL);
}
updateLayoutStateNeeded |= modelChanged;
}
/* If the JList.selectionModel property changes, remove our listener,
* selectionL from the old selectionModel and add it to the new one.
*/
else if (propertyName.equals("selectionModel")) {
ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
if (oldModel != null) {
oldModel.removeListSelectionListener(selectionL);
}
if (newModel != null) {
newModel.addListSelectionListener(selectionL);
}
updateLayoutStateNeeded |= modelChanged;
}
else if (propertyName.equals("cellRenderer")) {
updateLayoutStateNeeded |= cellRendererChanged;
}
else if (propertyName.equals("font")) {
updateLayoutStateNeeded |= fontChanged;
}
else if (propertyName.equals("prototypeCellValue")) {
updateLayoutStateNeeded |= prototypeCellValueChanged;
}
else if (propertyName.equals("fixedCellHeight")) {
updateLayoutStateNeeded |= fixedCellHeightChanged;
}
else if (propertyName.equals("fixedCellWidth")) {
updateLayoutStateNeeded |= fixedCellWidthChanged;
}
else if (propertyName.equals("cellRenderer")) {
updateLayoutStateNeeded |= cellRendererChanged;
}
}
}
/**
* Creates an instance of PropertyChangeListener that's added to
* the JList by installUI(). Subclasses can override this method
* to return a custom PropertyChangeListener, e.g.
* <pre>
* class MyListUI extends BasicListUI {
* protected PropertyChangeListener <b>createPropertyListener</b>() {
* return new MyPropertyListener();
* }
* public class MyPropertyListener extends PropertyListener {
* public void propertyChange(PropertyChangeEvent e) {
* if (e.getPropertyName().equals("model")) {
* // do some extra work when the model changes
* }
* super.propertyChange(e);
* }
* }
* }
* </pre>
*
* @see PropertyListener
* @see #installUI
*/
protected PropertyChangeListener createPropertyListener() {
return new PropertyListener();
}
}